﻿using CMS.Constants;
using CMS.Controls.Events;
using CMS.Dialogs;
using CMS.Infos;
using CMS.REST;
using CMS.Util;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using static CMS.Controls.ScheduleDisplay;

namespace CMS.Controls.DisplayTabPages
{
    public partial class DutyTabPage : MainFormTabPageTemplate
    {
        #region 内部类
        /// <summary>
        /// 值班信息适配器
        /// </summary>
        private class DutyAdapter : IAdapter, IDisposable
        {
            /// <summary>
            /// 所有预定义的画刷
            /// </summary>
            private readonly Brush[] MyBrushes = new Brush[] {
                new SolidBrush(ColorTranslator.FromHtml("#ffe66f")),
                new SolidBrush(ColorTranslator.FromHtml("#b9b9ff")),
                new SolidBrush(ColorTranslator.FromHtml("#b3d9d9")),
                new SolidBrush(ColorTranslator.FromHtml("#bce67e")),
                new SolidBrush(ColorTranslator.FromHtml("#ffa6ff")),
                new SolidBrush(ColorTranslator.FromHtml("#e1c4c4")),
                new SolidBrush(ColorTranslator.FromHtml("#81deb6")),
                new SolidBrush(ColorTranslator.FromHtml("#ff7575")),
                new SolidBrush(ColorTranslator.FromHtml("#ffd1a4")),
                new SolidBrush(ColorTranslator.FromHtml("#97cbff")),
                new SolidBrush(ColorTranslator.FromHtml("#d1d1f0")),
                new SolidBrush(ColorTranslator.FromHtml("#ffd265")),
                new SolidBrush(ColorTranslator.FromHtml("#d3a4ff")),
                new SolidBrush(ColorTranslator.FromHtml("#d6d6ad")),
                new SolidBrush(ColorTranslator.FromHtml("#bbffbb")),
                new SolidBrush(ColorTranslator.FromHtml("#ffaad5")),
                new SolidBrush(ColorTranslator.FromHtml("#ffbd9d")),
                new SolidBrush(ColorTranslator.FromHtml("#83d8dd")),
                new SolidBrush(ColorTranslator.FromHtml("#dab1d5")),
                new SolidBrush(ColorTranslator.FromHtml("#d0d0d0"))
            };

            /// <summary>
            /// 标题格式
            /// </summary>
            private readonly StringFormat _TitleFormat = new StringFormat() { Alignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap };

            /// <summary>
            /// 标题字体
            /// </summary>
            private readonly Font _TitleFont = new Font("微软雅黑", 12, GraphicsUnit.Pixel);

            /// <summary>
            /// 当元素被选中时的颜色
            /// </summary>
            private readonly Pen _SelectedPen = new Pen(Color.Gray, 1);

            /// <summary>
            /// 当元素被选中时的画刷
            /// </summary>
            private readonly Brush _SelectedBrush = new SolidBrush(Color.FromArgb(60, 0, 0, 0));

            /// <summary>
            /// 文字画刷
            /// </summary>
            private readonly Brush TitleBrush = Brushes.White;

            /// <summary>
            /// 记录下一个画刷的序号
            /// </summary>
            private int _ColorIndex = 0;

            /// <summary>
            /// 获取下一个画刷
            /// </summary>
            /// <returns></returns>
            private Brush _NextBrush()
            {
                return MyBrushes[_ColorIndex++ % MyBrushes.Length];
            }

            /// <summary>
            /// 颜色字典
            /// </summary>
            private readonly Dictionary<int, Brush> _ColorDict = new Dictionary<int, Brush>();

            /// <summary>
            /// 需要显示的值班安排,此变量作为缓存结果使用
            /// </summary>
            private readonly List<DutyArrange>[] _CurDisplay = new List<DutyArrange>[7] {
                new List<DutyArrange>(),
                new List<DutyArrange>(),
                new List<DutyArrange>(),
                new List<DutyArrange>(),
                new List<DutyArrange>(),
                new List<DutyArrange>(),
                new List<DutyArrange>()
            };

            /// <summary>
            /// 不同的助理染上不同的颜色
            /// </summary>
            /// <param name="arrange"></param>
            private void _SetColor(DutyArrange arrange)
            {
                if (!_ColorDict.TryGetValue(arrange.AssistantID, out Brush brush))
                {
                    brush = _NextBrush();
                    _ColorDict.Add(arrange.AssistantID, brush);
                }
                arrange.Tag = brush;
            }

            /// <summary>
            /// 存储当前需要显示的周次
            /// </summary>
            private uint _ClassWeek = 0;

            /// <summary>
            /// 需要进行显示的数据
            /// </summary>
            public uint ClassWeek
            {
                get => _ClassWeek; set
                {
                    _ClassWeek = value;
                    DataChanged?.Invoke(this, new ScheduleDisplayEventArgs(0));
                }
            }

            /// <summary>
            /// 当数据方式改变时,发射此事件来使ScheduleDisplay获得通知
            /// </summary>
            public event EventHandler<ScheduleDisplayEventArgs> DataChanged;

            /// <summary>
            /// 设置待显示的值班信息
            /// </summary>
            /// <param name="arrangs"></param>
            /// <param name="assistant"></param>
            public void SetDutyArrage(DutyArrange[] arrangs, Assistant[] assistant)
            {
                if (arrangs == null)
                {
                    for (int i = 0; i < 7; i++)
                    {
                        _CurDisplay[i].Clear();
                    }
                }
                else
                {
                    arrangs = arrangs.Join(assistant, a => a.AssistantID, b => b.ID, (a, b) =>
                    {
                        a.Name = b.Name;
                        _SetColor(a);
                        return a;
                    }).ToArray();

                    for (int i = 1; i <= 7; i++)
                    {
                        _CurDisplay[i - 1].Clear();
                        _CurDisplay[i - 1].AddRange(from d in arrangs where d.Week == i select d);
                    }
                }
                DataChanged?.Invoke(this, new ScheduleDisplayEventArgs(0));
            }

            /// <summary>
            /// 获取指定位置激活的元素
            /// </summary>
            /// <param name="selectPoint"></param>
            /// <returns></returns>
            public DutyArrange[] GetActiveItem(Point selectPoint)
            {
                if (selectPoint.X >= 1 && selectPoint.X <= 7 && selectPoint.Y >= 1 && selectPoint.Y <= 14)
                {
                    int week = selectPoint.X - 1;
                    int time = selectPoint.Y;
                    return (from item in _CurDisplay[week] where item.DutyStart <= time && time < item.DutyStart + item.DutyTimeSpan && (_ClassWeek & item.DutyClassWeekRaw) != 0 select item).ToArray();
                }
                else
                {
                    return new DutyArrange[0];
                }
            }

            /// <summary>
            /// 获取指定星期的值班安排
            /// </summary>
            /// <param name="week"></param>
            /// <returns></returns>
            public IEnumerable<DutyArrange> GetArranges(int week)
            {
                return _CurDisplay[week - 1];
            }

            /// <summary>
            /// 添加一个值班安排
            /// </summary>
            public void AddDuty(DutyArrange arrange)
            {
                _SetColor(arrange);
                _CurDisplay[arrange.Week - 1].Add(arrange);
                DataChanged?.Invoke(this, new ScheduleDisplayEventArgs(arrange.Week));
            }

            /// <summary>
            /// 移除一个值班安排
            /// </summary>
            public void RemoveDuty(DutyArrange arrange)
            {
                _CurDisplay[arrange.Week - 1].Remove(arrange);
                DataChanged?.Invoke(this, new ScheduleDisplayEventArgs(arrange.Week));
            }

            /// <summary>
            /// 当ScheduleDisplay需要更新指定列的显示时,调用此函数
            /// </summary>
            /// <param name="week"></param>
            /// <param name="pw"></param>
            /// <param name="ph"></param>
            /// <param name="selected"></param>
            /// <param name="g"></param>
            public void DrawItem(int week, int pw, int ph, Point selected, Graphics g)
            {
                foreach (DutyArrange item in _CurDisplay[week - 1])
                {
                    if ((_ClassWeek & item.DutyClassWeekRaw) != 0)
                    {
                        Rectangle rect = new Rectangle(week * pw + 1, item.DutyStart * ph + 1, pw - 1, ph * item.DutyTimeSpan - 1);

                        GraphicsPath path = Utils.GetRoundRect(rect.X, rect.Y, rect.Width, rect.Height, 3);
                        g.FillPath((Brush)item.Tag, path);
                        if (item.Week == selected.X && item.DutyStart <= selected.Y && selected.Y < item.DutyStart + item.DutyTimeSpan)
                        {
                            path = Utils.GetRoundRect(rect.X + 1, rect.Y + 1, rect.Width - 2, rect.Height - 2, 3);
                            g.FillPath(_SelectedBrush, path);
                            g.DrawPath(_SelectedPen, path);
                        }
                        rect.X += 2;
                        rect.Y += 6;
                        rect.Width -= 4;
                        rect.Height -= 12;
                        g.DrawString($"{item.Name}\n{item.DutyClassWeek}周\n[{item.DutyClassTime}]节", _TitleFont, TitleBrush, rect, _TitleFormat);
                    }
                }
            }

            private void Dispose(bool isDisposing)
            {
                if (isDisposing)
                {
                    _SelectedBrush.Dispose();
                    _SelectedPen.Dispose();
                    _TitleFont.Dispose();
                    _TitleFont.Dispose();
                }
            }

            public void Dispose()
            {
                Dispose(true);
            }
            ~DutyAdapter()
            {
                Dispose(false);
            }
        }

        /// <summary>
        /// 用于去除排班重复的比较器
        /// </summary>
        private class ClassWeekEQ : IEqualityComparer<DutyArrange>
        {
            /// <summary>
            /// 单例对象获取
            /// </summary>
            public static ClassWeekEQ Instance { get; } = new ClassWeekEQ();

            public bool Equals(DutyArrange x, DutyArrange y)
            {
                return x.AssistantID == y.AssistantID &&
                     (x.DutyClassWeekRaw & y.DutyClassWeekRaw) != 0 &&
                     (x.DutyClassTimeRaw & y.DutyClassTimeRaw) != 0 &&
                     x.Week == y.Week;
            }

            public int GetHashCode(DutyArrange obj)
            {
                return obj.AssistantID ^ obj.Week;
            }
        }

        /// <summary>
        /// 助理相关的额外上下文
        /// </summary>
        private class AssistantContext
        {
            /// <summary>
            /// 助理对象
            /// </summary>
            public Assistant Assistant;

            /// <summary>
            /// 表示助理除了需要上课之外的其他需要占用时间情况,第一维周次(0-19),第二维星期(0-6),而数字是位标记表示的节次
            /// </summary>
            public uint[][] BusyCache0;

            /// <summary>
            /// 空闲时间缓存,第一维表示星期,数字是位标记
            /// </summary>
            public uint[] IdleCache;

            /// <summary>
            /// 上课课程忙时
            /// </summary>
            public int[] BusyCourseSummary = new int[2];

            /// <summary>
            /// 值班安排忙时
            /// </summary>
            public int[] BusyArrangeSummary = new int[2];

            ///// <summary>
            ///// 工作日忙时间统计
            ///// </summary>
            //public int BusyWorkDaySummary;

            ///// <summary>
            ///// 周末忙时间统计
            ///// </summary>
            //public int BusyWeekDaySummary;

            ///// <summary>
            ///// 已排时间统计
            ///// </summary>
            //public int ArrangeSummary;

            /// <summary>
            /// 关联的列表元素
            /// </summary>
            public ListViewItem LVI;

            /// <summary>
            /// 初始化一个新的管理元素
            /// </summary>
            /// <param name="assistant"></param>
            /// <param name="lvi"></param>
            public AssistantContext(Assistant assistant, ListViewItem lvi)
            {
                Assistant = assistant;
                lvi.Tag = this;
                LVI = lvi;
            }
        }
        #endregion

        #region 私有字段
        /// <summary>
        /// 值班信息适配器
        /// </summary>
        private readonly DutyAdapter _DutyAdapter = new DutyAdapter();

        /// <summary>
        /// 存储了所有助理的信息
        /// </summary>
        private Assistant[] _Assistants = new Assistant[0];

        #endregion

        #region 初始化相关
        /// <summary>
        /// 构造器
        /// </summary>
        public DutyTabPage()
        {
            InitializeComponent();
            _Initialize();
        }

        /// <summary>
        /// 额外的界面初始化代码
        /// </summary>
        private void _Initialize()
        {
            List<KeyValuePair<string, uint>> classWeeks = Constant.GenClassWeekList();
            CB_ClassWeek.DataSource = classWeeks;
            _DutyAdapter.ClassWeek = classWeeks[0].Value;

            SD_Duty.ContextMenuStrip = CMS_Duty;
            SD_Duty.Adapter = _DutyAdapter;

            Bn_EditDuty.Enabled = Permission.CheckPermission(Permission.DutyArrange);
            LV_Summary.ListViewItemSorter = new MyComparer(_AssistantContextDictionary);

            Task.Run(() =>
            {
                _RefreshBasicData();
            });
        }

        /// <summary>
        /// 对标签的名字进行修改,必须要挂载了TabPage之后才能进行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DutyTabPage_ParentChanged(object sender, EventArgs e)
        {
            if (Parent != null)
            {
                Parent.Text = "值班安排表(只读)";
            }
        }
        #endregion

        #region 数据刷新

        /// <summary>
        /// 刷新基础数据
        /// </summary>
        private bool _RefreshBasicData()
        {
            bool result = true;
            RESTResponse response = RESTClient.IndexManaged("apis/v1/terms");
            if (response.IsSuccess)
            {
                Term[] terms = response.Data.ToObject<Term[]>();
                CB_Term.Invoke(() =>
                {
                    CB_Term.SyncComboBoxData("ID", terms);
                });
            }
            else
            {
                result = false;
                CB_Term.Invoke(() =>
                {
                    CB_Term.SyncComboBoxData("ID", new Term[0]);
                });
            }

            response = RESTClient.HttpGetManaged("apis/v1/building_names");
            if (response.IsSuccess)
            {
                string[] buildings = response.Data.ToObject<string[]>();
                CB_Building.Invoke(() =>
                {
                    CB_Building.SyncComboBoxData(null, buildings);
                });
            }
            else
            {
                result = false;
                CB_Building.Invoke(() =>
                {
                    CB_Building.SyncComboBoxData(null, new string[0]);
                });
            }

            response = RESTClient.IndexManaged("apis/v1/assistants");
            if (response.IsSuccess)
            {
                _Assistants = response.Data.ToObject<Assistant[]>();
            }
            else
            {
                result = false;
                _Assistants = new Assistant[0];
            }

            _RefreshDutyData(false);
            return result;
        }

        /// <summary>
        /// 刷新值班信息
        /// </summary>
        private bool _RefreshDutyData(bool reportError)
        {
            int termID = 0;
            string building = null;
            int result = this.Invoke(() =>
            {
                if (CB_Term.SelectedIndex == -1)
                {
                    if (reportError)
                    {
                        MessageBox.Show(this, "必须选择一个学期才能进行编辑", "错误");
                    }
                    return 1;
                }
                else
                {
                    termID = (int)CB_Term.SelectedValue;
                }
                if (CB_Building.SelectedIndex == -1)
                {
                    if (reportError)
                    {
                        MessageBox.Show(this, "必须选择一个地点才能进行编辑", "错误");
                    }
                    return 2;
                }
                else
                {
                    building = (string)CB_Building.SelectedItem;
                }
                return 0;
            });
            if (result != 0)
            {
                return false;
            }

            RESTResponse response = RESTClient.IndexManaged("apis/v1/duty_arranges", new RESTWhere("term_id", termID), new RESTWhere("building", building));
            if (response.IsSuccess)
            {
                DutyArrange[] arranges = response.Data.ToObject<DutyArrange[]>();
                _DutyAdapter.SetDutyArrage(arranges, _Assistants);
                return true;
            }
            else
            {
                _DutyAdapter.SetDutyArrage(new DutyArrange[0], _Assistants);
                return false;
            }
        }


        /// <summary>
        /// 刷新预览框内容
        /// </summary>
        private void _RefreshPreviewData()
        {
            LV_Preview.BeginUpdate();
            LV_Preview.Items.Clear();
            if (SD_Duty.SelectedPoint.X != 0 && SD_Duty.SelectedPoint.Y != 0)
            {
                foreach (DutyArrange item in _DutyAdapter.GetActiveItem(SD_Duty.SelectedPoint))
                {
                    LV_Preview.Items.Add(_GenListView(item));
                }
            }

            LV_Preview.EndUpdate();
        }


        #endregion

        #region 预览框信息生成
        /// <summary>
        /// 预览框元素主字体
        /// </summary>
        private static readonly Font _TitleFont = new Font("微软雅黑", 16, FontStyle.Bold, GraphicsUnit.Pixel);

        /// <summary>
        /// 预览框元素副字体
        /// </summary>
        private static readonly Font _ItemFont = new Font("微软雅黑", 12, GraphicsUnit.Pixel);

        /// <summary>
        /// 生成预览框元素
        /// </summary>
        /// <param name="arrange"></param>
        /// <returns></returns>
        private ListViewItem _GenListView(DutyArrange arrange)
        {
            ListViewItem lvi = new ListViewItem
            {
                Text = arrange.Name,
                UseItemStyleForSubItems = false,
                Font = _TitleFont,
                ForeColor = Color.DarkBlue,
                ImageKey = "Duty48"
            };

            lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, $"{arrange.DutyClassWeek}周 [{arrange.DutyClassTime}节]") { Font = _ItemFont });
            return lvi;

        }
        #endregion

        #region 信息选择框
        /// <summary>
        /// 学期改变时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CB_Term_SelectedIndexChanged(object sender, EventArgs e)
        {
            _RefreshDutyData(false);
        }

        /// <summary>
        /// 地点改变时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CB_Building_SelectedIndexChanged(object sender, EventArgs e)
        {
            _RefreshDutyData(false);
        }

        /// <summary>
        /// 周次改变时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CB_ClassWeek_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (_Editing)
            {
                uint classWeek = (uint)CB_ClassWeek.SelectedValue;
                _DutyAdapter.ClassWeek = classWeek;
                _RefreshIdleCache(classWeek);
            }
            else
            {
                //_RefreshDutyData(false);
                uint classWeek = (uint)CB_ClassWeek.SelectedValue;
                _DutyAdapter.ClassWeek = classWeek;
                _RefreshPreviewData();
            }
        }
        #endregion

        #region 值班安排相关
        /// <summary>
        /// 表示当前是否在编辑状态
        /// </summary>
        private bool _Editing = false;

        /// <summary>
        /// 当前正在处理的教学楼的名称
        /// </summary>
        private string _Building = null;

        /// <summary>
        /// 当前正在处理的周次的位标记
        /// </summary>
        private uint _ClassWeek = 0;

        /// <summary>
        /// 当前正在处理的学期的ID(用于保存数据时)
        /// </summary>
        private int _TermID = 0;

        /// <summary>
        /// 助理的课程信息
        /// </summary>
        private CourseAssistant[] _Courses = null;

        /// <summary>
        /// 上下文信息
        /// </summary>
        private Dictionary<int, AssistantContext> _AssistantContextDictionary = new Dictionary<int, AssistantContext>();

        /// <summary>
        /// 刷新助理的统计信息
        /// </summary>
        private void _InitAssistantContext()
        {
            _AssistantContextDictionary.Clear();
            LV_Summary.BeginUpdate();
            LV_Summary.Items.Clear();

            foreach (Assistant a in _Assistants)
            {
                ListViewItem lvi = new ListViewItem(a.Name)
                {
                    Font = _TitleFont,
                    ForeColor = Color.DarkBlue,
                    UseItemStyleForSubItems = false,
                };

                lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, "空闲时数") { ForeColor = Color.Gray });
                lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, "已排时数") { ForeColor = Color.Gray });
                lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, "工作日忙时数") { ForeColor = Color.Gray });
                lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, "周末日忙时数") { ForeColor = Color.Gray });

                LV_Summary.Items.Add(lvi);
                _AssistantContextDictionary.Add(a.ID, new AssistantContext(a, lvi));
            }
            LV_Summary.EndUpdate();
        }

        /// <summary>
        /// 刷新全部摘要信息
        /// </summary>
        private void _RefreshSummary()
        {
            Debug.Assert(_ClassWeek != 0);
            int classWeekCount = Math.Max(1, Utils.CalcBitmapNonZeroCount(_ClassWeek));
            int selectSumTime = classWeekCount * 7 * 14;
            LV_Summary.BeginUpdate();
            foreach (AssistantContext context in _AssistantContextDictionary.Values)
            {
                int arrangeSum = context.BusyArrangeSummary[0] + context.BusyArrangeSummary[1];
                int idleSum = selectSumTime - context.BusyCourseSummary[0] - context.BusyCourseSummary[1] - arrangeSum;
                int workdaySum = context.BusyArrangeSummary[0] + context.BusyCourseSummary[0];
                int weekdaySum = context.BusyArrangeSummary[1] + context.BusyCourseSummary[1];
                context.LVI.SubItems[1].Text = $"总空闲节数:{idleSum}节(约每周{idleSum / classWeekCount}节)";
                context.LVI.SubItems[2].Text = $"已排班节数:{arrangeSum}(约每周{arrangeSum / classWeekCount}节)";
                context.LVI.SubItems[3].Text = $"工作日忙节数:{workdaySum}(约每周{workdaySum / classWeekCount}节)";
                context.LVI.SubItems[4].Text = $"周末日忙节数:{weekdaySum}(约每周{weekdaySum / classWeekCount}节)";
            }
            LV_Summary.EndUpdate();
        }

        /// <summary>
        /// 刷新摘要信息(单个摘要)
        /// </summary>
        private void _RefreshSummary(int assistantID)
        {
            if (_AssistantContextDictionary.TryGetValue(assistantID, out AssistantContext context))
            {
                int classWeekCount = Math.Max(1, Utils.CalcBitmapNonZeroCount(_ClassWeek));

                int arrangeSum = context.BusyArrangeSummary[0] + context.BusyArrangeSummary[1];
                int idleSum = classWeekCount * 7 * 14 - context.BusyCourseSummary[0] - context.BusyCourseSummary[1] - arrangeSum;
                int workdaySum = context.BusyArrangeSummary[0] + context.BusyCourseSummary[0];
                int weekdaySum = context.BusyArrangeSummary[1] + context.BusyCourseSummary[1];
                context.LVI.SubItems[1].Text = $"总空闲节数:{idleSum}节(约每周{idleSum / classWeekCount}节)";
                context.LVI.SubItems[2].Text = $"已排班节数:{arrangeSum}(约每周{arrangeSum / classWeekCount}节)";
                context.LVI.SubItems[3].Text = $"工作日忙节数:{workdaySum}(约每周{workdaySum / classWeekCount}节)";
                context.LVI.SubItems[4].Text = $"周末日忙节数:{weekdaySum}(约每周{weekdaySum / classWeekCount}节)";
            }
        }

        /// <summary>
        /// 开始编辑
        /// </summary>
        private bool _StartEditDuty(string buidling, int termID, uint classWeek)
        {
            RESTResponse response = RESTClient.IndexManaged("apis/v1/courses_assistant", new RESTWhere("term_id", termID));
            if (response.IsSuccess)
            {
                _Courses = response.Data.ToObject<CourseAssistant[]>();
            }
            else
            {
                return false;
            }

            _Building = buidling ?? throw new ArgumentNullException(nameof(_Building));
            _TermID = termID;
            _DutyAdapter.ClassWeek = classWeek;


            _InitAssistantContext();
            _RefreshArrangeCache();
            _RefreshIdleCache(classWeek);

            return true;
        }

        /// <summary>
        /// 结束编辑(进行保存)
        /// </summary>
        /// <returns></returns>
        private bool _SaveEditDuty()
        {
            if (MessageBox.Show(this, "是否要保存编辑后的值班表,旧的值班表将会被直接覆盖", "询问", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                //合并内容
                List<DutyArrange> arranges = new List<DutyArrange>();
                for (int week = 1; week <= 7; week++)
                {
                    foreach (var g1 in from g in _DutyAdapter.GetArranges(week) group g by new { g.AssistantID, g.DutyClassTime })
                    {
                        uint classWeek = 0;
                        foreach (DutyArrange g2 in g1)
                        {
                            classWeek |= g2.DutyClassWeekRaw;
                        }
                        DutyArrange arrange = (DutyArrange)g1.First().Clone();
                        arrange.DutyClassWeekRaw = classWeek;
                        arranges.Add(arrange);
                    }
                }
                RESTResponse response = RESTClient.SaveListManaged("apis/v1/duty_arranges", JArray.FromObject(arranges), new RESTWhere("term_id", _TermID), new RESTWhere("building", _Building));
                if (response.IsSuccess)
                {

                    MessageBox.Show(this, "保存值班安排表成功", "保存成功");
                    return true;
                }
                else
                {
                    MessageBox.Show(this, "保存值班安排表失败,原因:" + response.ErrMsg, "保存失败");
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 刷新已排班缓存,此函数应当在开始编辑时进行调用,编辑过程中不要调用此函数
        /// </summary>
        /// <returns></returns>
        private void _RefreshArrangeCache()
        {
            //刷新其他地点同时期排课数据,用于防止在多个地点被排班
            RESTResponse response = RESTClient.IndexManaged("apis/v1/duty_arranges", new RESTWhere("term_id", _TermID), new RESTWhere("building", "<>", _Building));
            if (response.IsSuccess)
            {
                DutyArrange[] arranges = response.Data.ToObject<DutyArrange[]>();
                foreach (IGrouping<int, DutyArrange> g1 in from arrange in arranges group arrange by arrange.AssistantID)
                {
                    uint[][] tmp = new uint[20][];
                    foreach (DutyArrange g2 in g1)
                    {
                        foreach (int g3 in Utils.SplitBitmapFlag(g2.DutyClassWeekRaw))
                        {
                            if (tmp[g3] == null)
                            {
                                tmp[g3] = new uint[7];
                            }
                            tmp[g3][g2.Week - 1] |= g2.DutyClassTimeRaw;
                        }
                    }

                    if (!_AssistantContextDictionary.TryGetValue(g1.Key, out AssistantContext context))
                    {
                        continue;
                    }
                    context.BusyCache0 = tmp;
                }
            }
            else
            {
                Debug.WriteLine("刷新其他地点同时期排课数据没有成功,可能导致排班结果冲突", "WARN");
            }
        }



        /// <summary>
        /// 刷新忙标记缓存(需要在选择周次发生改变时进行,此操作代价非常大,仅用于编辑状态时)
        /// </summary>
        private void _RefreshIdleCache(uint classWeek)
        {
            foreach (Assistant assistant in _Assistants)
            {
                if (!_AssistantContextDictionary.TryGetValue(assistant.ID, out AssistantContext context))
                {
                    Debug.WriteLine($"找不到序号为:{assistant.ID}的助理,即将跳过");
                    continue;
                }

                uint[] busyTime = new uint[7];          //将指定排课范围内的课程占用情况投射到一维

                int workdayCourseSum = 0;
                int weekdayCourseSum = 0;
                int workdayArrangeSum = 0;
                int weekdayArrangeSum = 0;

                //对曾经排班过的数据进行排斥,避免重复排班
                if (context.BusyCache0 != null)
                {
                    uint[][] flags = context.BusyCache0;
                    foreach (int a in Utils.SplitBitmapFlag(classWeek))
                    {
                        uint[] flag = flags[a];
                        if (flag != null)
                        {
                            for (int i = 0; i < 5; i++)
                            {
                                busyTime[i] |= flag[i];
                                workdayArrangeSum += Utils.CalcBitmapNonZeroCount(flag[i]);
                            }
                            for (int i = 5; i < 7; i++)
                            {
                                busyTime[i] |= flag[i];
                                weekdayArrangeSum += Utils.CalcBitmapNonZeroCount(flag[i]);
                            }
                        }
                    }
                }

                //统计助理上课时间
                foreach (var g in from course in _Courses
                                  where assistant.ID == course.AssistantID && (classWeek & course.ClassWeekRaw) != 0
                                  group new { course.ClassTimeRaw, course.ClassWeekRaw } by course.Week)
                {
                    foreach (var i in g)
                    {
                        busyTime[g.Key - 1] |= i.ClassTimeRaw;
                        if (g.Key <= 5)
                        {
                            workdayCourseSum += Utils.CalcBitmapNonZeroCount(i.ClassTimeRaw) * Utils.CalcBitmapNonZeroCount(i.ClassWeekRaw & classWeek);
                        }
                        else
                        {
                            weekdayCourseSum += Utils.CalcBitmapNonZeroCount(i.ClassTimeRaw) * Utils.CalcBitmapNonZeroCount(i.ClassWeekRaw & classWeek);
                        }
                    }
                }

                //对当前正在编辑的排课信息进行统计
                for (int i = 1; i <= 5; i++)
                {
                    foreach (DutyArrange g in from duty in _DutyAdapter.GetArranges(i)
                                              where duty.AssistantID == assistant.ID &&
                                                (duty.DutyClassWeekRaw & classWeek) != 0
                                              select duty)
                    {
                        workdayArrangeSum += Utils.CalcBitmapNonZeroCount(g.DutyClassWeekRaw) * Utils.CalcBitmapNonZeroCount(g.DutyClassTimeRaw);
                    }
                }
                for (int i = 6; i <= 7; i++)
                {
                    foreach (DutyArrange g in from duty in _DutyAdapter.GetArranges(i)
                                              where duty.AssistantID == assistant.ID &&
                                                (duty.DutyClassWeekRaw & classWeek) != 0
                                              select duty)
                    {
                        weekdayArrangeSum += Utils.CalcBitmapNonZeroCount(g.DutyClassWeekRaw) * Utils.CalcBitmapNonZeroCount(g.DutyClassTimeRaw);
                    }
                }

                //获得助理空闲时间位标记
                for (int i = 0; i < 7; i++)
                {
                    busyTime[i] = (~busyTime[i]) & 0x3FFF;
                }

                context.IdleCache = busyTime;
                context.BusyCourseSummary[0] = workdayCourseSum;
                context.BusyCourseSummary[1] = weekdayCourseSum;
                context.BusyArrangeSummary[0] = workdayArrangeSum;
                context.BusyArrangeSummary[1] = weekdayArrangeSum;
            }
            _ClassWeek = classWeek;
            _RefreshSummary();
        }

        /// <summary>
        /// 生成待编排的值班安排
        /// </summary>
        /// <param name="week">星期(1-7)</param>
        /// <param name="timePeriod">时间1-14(但是只有分3个阶段进行,分别为早上(1-6)下午(7-10)晚上(11-14))</param>
        private IEnumerable<DutyArrange> _GenDutyArrange(Point selectPoint)
        {
            int week = selectPoint.X - 1;
            IEnumerable<DutyArrange> _Gen(uint flag, uint flag1)
            {
                foreach (AssistantContext item in _AssistantContextDictionary.Values)
                {
                    if ((item.IdleCache[week] & flag) == flag)
                    {
                        yield return new DutyArrange() { TermID = _TermID, Building = _Building, AssistantID = item.Assistant.ID, Week = (ushort)selectPoint.X, DutyClassWeekRaw = _ClassWeek, DutyClassTimeRaw = flag, Name = item.Assistant.Name };
                    }
                    else if ((item.IdleCache[week] & flag1) == flag1)
                    {
                        yield return new DutyArrange() { TermID = _TermID, Building = _Building, AssistantID = item.Assistant.ID, Week = (ushort)selectPoint.X, DutyClassWeekRaw = _ClassWeek, DutyClassTimeRaw = flag1, Name = item.Assistant.Name };
                    }
                }
            }

            switch (selectPoint.Y)
            {
                case 1:
                case 2:
                    return _Gen(0xF, 0x3);
                case 3:
                case 4:
                    return _Gen(0xF, 0xC);
                case 5:
                case 6:
                    return _Gen(0x3F, 0x30);
                case 7:
                case 8:
                    return _Gen(0x3C0, 0xC0);
                case 9:
                case 10:
                    return _Gen(0x3C0, 0x300);
                case 11:
                case 12:
                    return _Gen(0x3C00, 0xC00);
                case 13:
                case 14:
                    return _Gen(0x3C00, 0x3000);
                default:
                    return Enumerable.Empty<DutyArrange>();
            }
        }

        /// <summary>
        /// 开始编辑按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Bn_EditDuty_Click(object sender, EventArgs e)
        {
            if (_Editing)
            {
                if (_SaveEditDuty())
                {
                    _Editing = false;
                }
            }
            else
            {
                if (!_RefreshDutyData(true))
                {
                    return;
                }
                if (_StartEditDuty((string)CB_Building.SelectedItem, (int)CB_Term.SelectedValue, (uint)CB_ClassWeek.SelectedValue))
                {
                    _Editing = true;
                }
            }
            if (_Editing)
            {
                //编辑状态中,需要开启所有功能
                Bn_EditDuty.Text = "保存";
                CB_Term.Enabled = CB_Building.Enabled = false;
                LV_Summary.Enabled = true;
                Parent.Text = "值班安排表(编辑中)";
                SD_Duty.BackColor = Color.White;
            }
            else
            {
                //只读状态中,只需要显示的功能
                Bn_EditDuty.Text = "编辑";
                CB_Term.Enabled = CB_Building.Enabled = true;
                LV_Summary.Enabled = false;
                LV_Summary.Items.Clear();
                Parent.Text = "值班安排表(只读)";
                SD_Duty.BackColor = Color.WhiteSmoke;
            }
        }
        #endregion

        #region 课程表上下文菜单响应
        /// <summary>
        /// 上下文打开时,进行初始化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CMS_Duty_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!_Editing || SD_Duty.SelectedPoint.X == 0 || SD_Duty.SelectedPoint.Y == 0)
            {
                e.Cancel = true;
                return;
            }

            ToolStripItemCollection itemsAdd = TSMI_AddArrange.DropDownItems;
            ToolStripItemCollection itemDel = TSMI_CancelArrange.DropDownItems;
            ToolStripItemCollection itemSee = TSMI_SeeDetial.DropDownItems;
            ToolStripItemCollection itemEdit = TSMI_Edit.DropDownItems;

            itemsAdd.Clear();
            itemDel.Clear();
            itemSee.Clear();
            itemEdit.Clear();

            DutyArrange[] items = _GenDutyArrange(SD_Duty.SelectedPoint)
                .Except(_DutyAdapter.GetArranges(SD_Duty.SelectedPoint.X), ClassWeekEQ.Instance)
                .OrderBy(item => item.DutyTimeSpan >= 4u ? uint.MinValue : item.DutyClassTimeRaw)
                .ToArray();
            TSMI_AddArrange.Enabled = items.Count() > 0;

            foreach (DutyArrange item in items)
            {
                itemsAdd.Add(new ToolStripMenuItem($"{item.Name,-8} {item.DutyClassWeek}周[{item.DutyClassTime}]节", Resource1.Add16, TSMI_AddArrange_Click)
                {
                    Tag = item
                });
            }

            items = _DutyAdapter.GetActiveItem(SD_Duty.SelectedPoint);
            TSMI_CancelArrange.Enabled = TSMI_SeeDetial.Enabled = items.Length > 0;
            foreach (DutyArrange item in items)
            {
                string title = $"{item.Name,-8} {item.DutyClassWeek}周[{item.DutyClassTime}]节";
                itemDel.Add(new ToolStripMenuItem(title, Resource1.Sub16, TSMI_DelArrange_Click)
                {
                    Tag = item
                });

                itemSee.Add(new ToolStripMenuItem(title, Resource1.See16, TSMI_SeeDetial_Click)
                {
                    Tag = item
                });

                itemEdit.Add(new ToolStripMenuItem(title, Resource1.Edit16, TSMI_Edit_Click)
                {
                    Tag = item
                });
            }
        }

        /// <summary>
        /// 响应上下文菜单的
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TSMI_AddArrange_Click(object sender, EventArgs e)
        {
            if (((ToolStripMenuItem)sender).Tag is DutyArrange arrange)
            {
                _DutyAdapter.AddDuty(arrange);
                if (_AssistantContextDictionary.TryGetValue(arrange.AssistantID, out AssistantContext context))
                {
                    if (arrange.Week <= 5)
                    {
                        context.BusyArrangeSummary[0] += Utils.CalcBitmapNonZeroCount(arrange.DutyClassTimeRaw) * Utils.CalcBitmapNonZeroCount(arrange.DutyClassWeekRaw);
                    }
                    else
                    {
                        context.BusyArrangeSummary[1] += Utils.CalcBitmapNonZeroCount(arrange.DutyClassTimeRaw) * Utils.CalcBitmapNonZeroCount(arrange.DutyClassWeekRaw);
                    }
                }
                _RefreshSummary(arrange.AssistantID);
            }
        }

        /// <summary>
        /// 响应上下文菜单的添加
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TSMI_DelArrange_Click(object sender, EventArgs e)
        {
            if (((ToolStripMenuItem)sender).Tag is DutyArrange arrange)
            {
                _DutyAdapter.RemoveDuty(arrange);
                if (_AssistantContextDictionary.TryGetValue(arrange.AssistantID, out AssistantContext context))
                {
                    if (arrange.Week <= 5)
                    {
                        context.BusyArrangeSummary[0] -= Utils.CalcBitmapNonZeroCount(arrange.DutyClassTimeRaw) * Utils.CalcBitmapNonZeroCount(arrange.DutyClassWeekRaw);
                    }
                    else
                    {
                        context.BusyArrangeSummary[1] -= Utils.CalcBitmapNonZeroCount(arrange.DutyClassTimeRaw) * Utils.CalcBitmapNonZeroCount(arrange.DutyClassWeekRaw);
                    }
                }
                _RefreshSummary(arrange.AssistantID);
            }
        }

        private class MyComparer : IComparer
        {
            public int mode = 0;

            private readonly Dictionary<int, AssistantContext> Dict;

            private object Target = null;

            public MyComparer(Dictionary<int, AssistantContext> dict)
            {
                Dict = dict;
            }

            public void SetTop(object target)
            {
                mode = 0;
                Target = target;
            }

            public void SetSortByArrange()
            {
                mode = 1;
            }

            public void SetSortByIdleWorkDay()
            {
                mode = 2;
            }

            public int Compare(object x, object y)
            {
                if (x == null || y == null)
                {
                    return 0;
                }

                switch (mode)
                {
                    case 0:
                        if (x == Target)
                        {
                            return int.MinValue;
                        }
                        else if (y == Target)
                        {
                            return int.MaxValue;
                        }
                        else
                        {
                            return 0;
                        }
                    case 1:
                        {
                            AssistantContext context1 = (AssistantContext)((ListViewItem)x).Tag;
                            AssistantContext context2 = (AssistantContext)((ListViewItem)y).Tag;
                            return context1.BusyArrangeSummary[0] + context1.BusyArrangeSummary[1] - context2.BusyArrangeSummary[0] - context2.BusyArrangeSummary[1];
                        }
                    case 2:
                        {
                            AssistantContext context1 = (AssistantContext)((ListViewItem)x).Tag;
                            AssistantContext context2 = (AssistantContext)((ListViewItem)y).Tag;
                            return context1.BusyArrangeSummary[0] + context1.BusyCourseSummary[0] - context2.BusyArrangeSummary[0] - context2.BusyCourseSummary[0];
                        }
                    default:
                        return 0;
                }

            }

        }

        /// <summary>
        /// 查看详细信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TSMI_SeeDetial_Click(object sender, EventArgs e)
        {
            if (((ToolStripMenuItem)sender).Tag is DutyArrange arrange)
            {
                if (_AssistantContextDictionary.TryGetValue(arrange.AssistantID, out AssistantContext context))
                {
                    ((MyComparer)LV_Summary.ListViewItemSorter).SetTop(context.LVI);
                    LV_Summary.Sort();
                    int index = LV_Summary.Items.IndexOf(context.LVI);
                    if (index != -1)
                    {
                        LV_Summary.SelectedIndices.Add(index);
                        LV_Summary.Focus();
                    }
                }
            }
        }

        /**
         * 编辑值班信息
         */
        private void TSMI_Edit_Click(object sender, EventArgs e)
        {
            if (((ToolStripMenuItem)sender).Tag is DutyArrange arrange)
            {
                var dialog = new EditDutySpanDialog()
                {
                    AssistantName = arrange.Name,
                    DutySpan = arrange.DutyTimeSpan
                };
                if (dialog.ShowDialog(this) == DialogResult.OK)
                {
                    arrange.DutyClassTimeRaw = (uint)(((1 << (dialog.DutySpan)) - 1) << (arrange.DutyStart - 1));
                    _RefreshIdleCache(_ClassWeek);
                    SD_Duty.Refresh();
                }
            }
        }

        private void TSMI_ClearArrange_Click(object sender, EventArgs e)
        {
            Point selectPoint = SD_Duty.SelectedPoint;
            if (selectPoint.X == 0 || selectPoint.Y == 0)
            {
                return;
            }

            foreach (DutyArrange arrange in _DutyAdapter.GetArranges(selectPoint.X).ToArray())
            {
                _DutyAdapter.RemoveDuty(arrange);
                if (_AssistantContextDictionary.TryGetValue(arrange.AssistantID, out AssistantContext context))
                {
                    if (arrange.Week <= 5)
                    {
                        context.BusyArrangeSummary[0] -= Utils.CalcBitmapNonZeroCount(arrange.DutyClassTimeRaw) * Utils.CalcBitmapNonZeroCount(arrange.DutyClassWeekRaw);
                    }
                    else
                    {
                        context.BusyArrangeSummary[1] -= Utils.CalcBitmapNonZeroCount(arrange.DutyClassTimeRaw) * Utils.CalcBitmapNonZeroCount(arrange.DutyClassWeekRaw);
                    }
                }
            }
            _RefreshSummary();
        }

        #endregion

        #region 预览框部分响应

        /// <summary>
        /// 当选择的位置发生改变时,刷新预览框
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SD_Duty_SelectChanged(object sender, EventArgs e)
        {
            _RefreshPreviewData();

        }


        #endregion

        private void TSMI_SortByArrangeTime_Click(object sender, EventArgs e)
        {
            MyComparer comparer = LV_Summary.ListViewItemSorter as MyComparer;
            comparer.SetSortByArrange();
            LV_Summary.Sort();
        }

        private void TSMI_SortByIdle_Click(object sender, EventArgs e)
        {
            MyComparer comparer = LV_Summary.ListViewItemSorter as MyComparer;
            comparer.SetSortByIdleWorkDay();
            LV_Summary.Sort();
        }


    }
}
